<template>
  <div class=""></div>
</template>

<script setup lang="ts">
  //   import { ref, unref, reactive, onMounted } from 'vue'
  // 简易版深拷贝
  function deepClone(source: any): any {
    if (!source || typeof source !== 'object') return
    const targetObj: any = Array.isArray(source) ? [] : {}
    Object.keys(source).forEach((key: string) => {
      if (source[key] && typeof source[key] === 'object') {
        targetObj[key] = deepClone(source[key])
      } else {
        targetObj[key] = source[key]
      }
    })
    return targetObj
  }

  // 完整版深拷贝 =》1.循环引用 2.拷贝特殊对象（RegExp, Date, Set, Map等)

  const getType = obj => Object.prototype.toString.call(obj)
  const isObject = source => (typeof source === 'object' || typeof source === 'function') && source !== null

  const canTraverse = {
    '[object Map]': true,
    '[object Set]': true,
    '[object Array]': true,
    '[object Object]': true,
    '[object Arguments]': true
  }
  const mapTag = '[object Map]'
  const setTag = '[object Set]'
  const boolTag = '[object Boolean]'
  const numberTag = '[object Number]'
  const stringTag = '[object String]'
  const symbolTag = '[object Symbol]'
  const dateTag = '[object Date]'
  const errorTag = '[object Error]'
  const regexpTag = '[object RegExp]'

  const handleRegExp = (target: any) => {
    const { source, flags } = target
    return new target.constructor(source, flags)
  }
  const handleNotTraverse = (source: any, tag: any) => {
    let Ctor = source.constructor
    switch (tag) {
      case boolTag:
        return new Object(Boolean.prototype.valueOf.call(source))
      case numberTag:
        return new Object(Number.prototype.valueOf.call(source))
      case stringTag:
        return new Object(String.prototype.valueOf.call(source))
      case symbolTag:
        return new Object(Symbol.prototype.valueOf.call(source))
      case errorTag:
      case dateTag:
        return new Ctor(source)
      case regexpTag:
        return handleRegExp(source)
      default:
        return new Ctor(source)
    }
  }
  const deepCloneFull = (source: any, map = new WeakMap()) => {
    if (!isObject(source)) {
      return false
    }
    let type = getType(source)
    let targetObj: any
    if (!canTraverse[type]) {
      // 处理不能遍历的对象
      return handleNotTraverse(source, type)
    } else {
      // 这波操作相当关键，可保证对象的原型不丢失
      let ctor = source.constructor
      targetObj = new ctor()
    }

    if (map.get(source)) {
      return source
    }
    map.set(source, true)

    if (type === setTag) {
      // 处理Set
      source.forEach((item: any) => {
        targetObj.add(deepCloneFull(item, map))
      })
    }
    if (type === mapTag) {
      // 处理Map
      source.forEach((item: any, key: any) => {
        targetObj.set(deepCloneFull(key, map), deepCloneFull(item, map))
      })
    }

    // 处理数组和对象
    Object.keys(source).forEach((key: string) => {
      if (source[key] && typeof source[key] === 'object') {
        targetObj[key] = deepCloneFull(source[key], map)
      } else {
        targetObj[key] = source[key]
      }
    })
    return targetObj
  }

  //   使用深拷贝 - 对象
  const oldObj = {
    user_info: {
      name: 'Nancy',
      age: 18,
      gender: '女'
    },
    hobbies: ['eat', 'sleep', 'game'],
    // 函数
    watchComic: () => {
      console.log('Nancy 你好')
    },
    regx: /^icy{3}$/g,
    set: new Set(),
    map: new Map(),
    date: new Date()
  }
  const newObj = deepCloneFull(oldObj)
  newObj.user_info.name = '铁锤妹妹'
  newObj.hobbies[0] = 'photo'
  console.log(newObj, 'newObj')
  console.log(oldObj, 'oldObj')

  //   使用深拷贝 - 数组
  const oldArr = [
    {
      name: '铁锤1',
      age: 18,
      like: {
        drinks: 'milk'
      }
    },
    {
      name: '铁锤2',
      age: 19,
      like: {
        drinks: 'orange juice'
      }
    },
    {
      name: '铁锤3',
      age: 20,
      like: {
        drinks: 'coke'
      }
    }
  ]
  const newArr = deepCloneFull(oldArr)
  newArr[0].like.drinks = '喜茶'
  console.log(newArr, 'newArr')
  console.log(oldArr, 'oldArr')

  // 解决循环引用问题
  const a = { val: 100 } as { val: number; target?: any }
  a.target = a
  let newA = deepCloneFull(a)
  // 按照deepClone1中的写法遇到循环引用会报错 Maximum call stack size exceeded
  console.log(newA, 'clone循环引用')
</script>

<style lang="less" scoped></style>
